home *** CD-ROM | disk | FTP | other *** search
/ CD Actual 9 / CDACTUAL9.iso / share / Dos / VARIOS / pascal / SWAG9605.DDD / 0074_DELPHI CGI routines.pas < prev    next >
Encoding:
Pascal/Delphi Source File  |  1996-05-31  |  36.5 KB  |  1,113 lines

  1. unit Cgi;
  2.  
  3. { cgi.pas
  4.  
  5.   Author: Ann Lynnworth
  6.   Copyright (c) 1995-1996, Ann Lynnworth.  All Rights Reserved.
  7.  
  8.   Thanks to Fred Thompson for adding getSmallMultiField().
  9.  
  10.   Thanks to Dagur Georgsson for testing and debugging the
  11.   internationalization of getSmallField.
  12.  
  13.   This program may be freely used and modified by anyone.  It would be
  14.   considerate to keep at least my name and copyright notice intact.
  15.  
  16.   It is distributed with a "don't laugh at my code" disclaimer.
  17.   This was my first attempt at writing a Delphi component back
  18.   in June '95.  If I change it now, hundreds of web-applications
  19.   will break.  So I'm leaving the data structures alone.
  20.  
  21.   What would I do different?  For starters, I wouldn't use pstrings
  22.   on the published properties!
  23.  
  24.   URLs of note:
  25.  
  26.   http://super.sonic.net/ann/delphi/cgicomp/ -- home of this component
  27.   http://www.href.com/ -- home of my company, HREF Tools Corp., with newer cgi tools
  28.   http://website.ora.com/ -- home of WebSite 32-bit server
  29.   http://www.borland.com/ -- you remember Borland; they made Delphi for us <g>
  30. }
  31.  
  32. { Technical support -- sorry, there isn't any.  This is a FREE component.
  33.  
  34.   Here are the 3 things I usually tell people to get them started:
  35.  
  36.   1. download the free demo project from http://super.sonic.net/ann/delphi/cgicomp/code.html
  37.   2. If you're following the directions in cgihelp.hlp, make sure you also
  38.      connect the form create method to the form's onCreate event handler.
  39.      That's easy to overlook and of course your app won't work.
  40.   3. To test, you need to run the .exe from a browser using an http command
  41.      in the form: http://127.0.0.1/cgi-win/demo1.exe
  42.  
  43.      That IP references your local drive.  You can use any other IP
  44.      or domain name.
  45.  
  46.      You will not be able to test or debug your web-application within Delphi.
  47.  
  48.      These components do not work as-is with Netscape server, at least
  49.      not with Netscape's implementation of win-cgi as of 2/23/96.
  50.      They only work with WebSite server from O'Reilly & Associates.
  51. }
  52.  
  53. interface
  54.  
  55. uses
  56.   SysUtils, WinTypes, WinProcs, Messages, Classes,
  57.   forms, iniFiles, Dialogs;
  58.  
  59. type
  60.   NTWebServerType = (WebSite);   {only one choice; I meant to have more}
  61.  
  62. type
  63.   TWebServer = class(TComponent);
  64.  
  65. type
  66.   TCGIEnvData = class(TComponent)
  67.   private
  68.     { Private declarations -- custom for this component }
  69.     fServerType : NTWebServerType;
  70.     fServerComponent : TWebServer;
  71.     fStdOut : integer;
  72.     fAddress : string;
  73.     { for use with WebSite only }
  74.     finiFilename : string;
  75.     {CGI section of web site INI file}
  76.     fCGICGIVersion : string;
  77.     fCGIRequestProtocol : string;
  78.     fCGIRequestMethod : string;  { 'GET' or 'POST' -- should be POST }
  79.     fCGIExecutablePath : string;
  80.     fCGILogicalPath : string;
  81.     fCGIPhysicalPath : string;
  82.     fCGIQueryString : string;
  83.     fCGIContentType : string;
  84.     fCGIContentLength : longInt;
  85.     fCGIServerSoftware : string;
  86.     fCGIServerName : string;
  87.     fCGIServerPort : string;
  88.     fCGIServerAdmin : string;
  89.     fCGIReferer : string;
  90.     fCGIFrom : string;
  91.     fCGIRemoteHost : string;
  92.     fCGIRemoteAddress : string;
  93.     fCGIAuthenticatedUsername : string;
  94.     fCGIAuthenticatedPassword : string;
  95.     fCGIAuthenticationMethod : string;
  96.     fCGIAuthenticationRealm : string;
  97.     fSystemGMTOffset : double;
  98.     fSystemOutputFile : string;
  99.     fSystemContentFile : string;
  100.     fSystemDebugMode : string;
  101.     {Custom Private Procedures & Functions }
  102.     procedure getCGIItem( p : pString; key : string; okEmpty : boolean );
  103.     { CGI }
  104.     function  getCGICGIVersion : pstring;
  105.     function  getCGIRequestProtocol : pstring;
  106.     function  getCGIRequestMethod : pstring;
  107.     function  getCGIExecutablePath : pstring;
  108.     function  getCGILogicalPath : pstring;
  109.     function  getCGIPhysicalPath : pString;
  110.     function  getCGIQueryString : pString;
  111.     function  getCGIContentType : pString;
  112.     function  getCGIContentLength : longInt;
  113.     function  getCGIServerSoftware : pstring;
  114.     function  getCGIServerName : pstring;
  115.     function  getCGIServerPort : pString;
  116.     function  getCGIServerAdmin : pString;
  117.     function  getCGIReferer : pString;
  118.     function  getCGIFrom : pString;
  119.     function  getCGIRemoteHost : pString;
  120.     function  getCGIRemoteAddress : pString;
  121.     function  getCGIAuthenticatedUsername : pString;
  122.     function  getCGIAuthenticatedPassword : pString;
  123.     function  getCGIAuthenticationMethod  : pString;
  124.     function  getCGIAuthenticationRealm   : pString;
  125.     { system }
  126.     {function  getSystemGMTOffset: pstring;}
  127.     function  getSystemOutputFile : pstring;
  128.     function  getSystemDebugMode : pstring;
  129.     function  getSystemContentFile : pstring;
  130.   protected
  131.     { Protected declarations }
  132.     constructor Create(AOwner: TComponent); override;
  133.     destructor  Destroy; override;
  134.   public
  135.     { Public declarations }
  136.     { misc }
  137.  
  138.     { WebSite only }
  139.     procedure setIniFilename( value : string );
  140.     {CGI}
  141.     { Regarding all these pstrings.  I didn't know better.  I was trying to save
  142.       255 bytes x this many properties.  "Don't laugh at my code."  Or laugh away,
  143.       just do it in private. <g> }
  144.     property  CGICGIVersion      : pstring read getCGICGIVersion stored false;
  145.     property  CGIRequestProtocol : pstring read getCGIRequestProtocol stored false;
  146.     property  CGIRequestMethod   : pstring read getCGIRequestMethod stored false;
  147.     property  CGIExecutablePath  : pstring read getCGIExecutablePath stored false;
  148.     property  CGILogicalPath     : pstring read getCGILogicalPath stored false;
  149.     property  CGIPhysicalPath    : pstring read getCGIPhysicalPath stored false;
  150.     property  CGIQueryString     : pString read getCGIQueryString stored false;
  151.     property  CGIContentType     : pString read getCGIContentType stored false;
  152.     property  CGIContentLength   : longInt read getCGIContentLength stored false;
  153.     property  CGIServerSoftware  : pString read getCGIServerSoftware stored false;
  154.     property  CGIServerPort      : pString read getCGIServerPort stored false;
  155.     property  CGIServerName      : pString read getCGIServerName stored false;
  156.     property  CGIServerAdmin     : pstring read getCGIServerAdmin stored false;
  157.     property  CGIReferer         : pString read getCGIReferer stored false;
  158.     property  CGIFrom            : pString read getCGIFrom stored false;
  159.     property  CGIRemoteHost      : pString read getCGIRemoteHost stored false;
  160.     property  CGIRemoteAddress   : pString read getCGIRemoteAddress stored false;
  161.     property  CGIAuthenticatedUsername : pString read getCGIAuthenticatedUsername stored false;
  162.     property  CGIAuthenticatedPassword : pString read getCGIAuthenticatedPassword stored false;
  163.     property  CGIAuthenticationMethod  : pString read getCGIAuthenticationMethod  stored false;
  164.     property  CGIAuthenticationRealm   : pString read getCGIAuthenticationRealm   stored false;
  165.     {System}
  166.     property  SystemGMToffset : double read fSystemGMToffset stored false;
  167.     property  SystemOutputFile : pstring read getSystemOutputFile stored false;
  168.     property  SystemContentFile : pstring read getSystemContentFile stored false;
  169.     property  SystemDebugMode : pstring read getSystemDebugMode stored false;
  170.   published
  171.     { Published declarations }
  172.  
  173.     { set this to your address, e.g. ann@href.com }
  174.     property    address : string read fAddress write fAddress;
  175.  
  176.     { ServerTypes WebSite and httpd16 are functionally equivalent. }
  177.     { The whole issue of ServerType is silly. }
  178.     { Property is left in for compatibility only. 1-Jan-96 }
  179.     property    ServerType : NTWebServerType read fServerType write fServerType;
  180.  
  181.     function    swapChar( s : string; fromChar : char; toChar : char ) : string;
  182.  
  183.     { set this to paramstr(1) at the beginning of your program }
  184.     property    webSiteIniFilename : string read finiFilename write setIniFilename;
  185.  
  186.     { ***************************** }
  187.  
  188.     { Use the LOCATION: URL feature to "bounce" a user to a URL }
  189.     procedure   bounceToLocation( goHere : string );
  190.  
  191.     { set application.onException to TCGIEnvData1.cgiErrorHandler as soon as you can in your app! }
  192.     procedure   cgiErrorHandler( sender : TObject; e : Exception ) ;
  193.  
  194.     { call this at the end of your program }
  195.     procedure   closeStdout;
  196.  
  197.     { This opens the stdout file based on filename created by WebSite;
  198.       if you forget this line, the first send command will take care of it
  199.       for you automatically. }
  200.     function    createStdout : boolean;
  201.  
  202.     { get data from a named External field {size between 255 and 64K chars
  203.       and put it into a PChar.  If you're basically working with input from
  204.       a TextArea on a form, see getTextArea below.  It will be much more
  205.       convenient. }
  206.     function    getExternalField( key : string; var externFilename : string; dest : PChar ) : boolean;
  207.  
  208.     { get everything in a section ('Form Literal' or 'Form External').  Refer to
  209.       readSectionValues in Delphi Help. This is the same thing -- it just automatically
  210.       goes to the correct INI file for you. }
  211.     function    getSectionValues( sectionName : string; strings : TStringList ) : boolean;
  212.  
  213.     { get data from an HTML form based on field name ("key") }
  214.     function    getSmallField( key : string ) : string;
  215.  
  216.     {***********************************************************************}
  217.     {*** getSmallMultiField - added Dec. 17, 1995 - Fred Thompson **********}
  218.     {***********************************************************************}
  219.     { get Multiple data from an HTML form based on field name ("key")       }
  220.     { Return value contains all the values selected.                        }
  221.     function    getSmallMultiField( key : string ) : Tstringlist;
  222.     {***********************************************************************}
  223.  
  224.     { TextAreas are tricky.  If the user only enters one line of text, that
  225.       text is stored as a "small field" in the [Form Literal] section.  This
  226.       function hides that complexity and lets you simply work with a string
  227.       list (which might only have one string in it).  }
  228.     function    getTextArea( key : string; dest : TStringList ) : boolean;
  229.  
  230.     { send a line of code to stdout (including required CR/LF) }
  231.     function    send( s : string ) : boolean ;
  232.     function    sendString( s : string; appendNewline : boolean ) : boolean;
  233.  
  234.     { send contents of Address property }
  235.     function    sendAddress : boolean;
  236.  
  237.     function    sendAuthRequest : boolean;
  238.  
  239.     { send wallpaper background command (HTML 3.0) -- no color control yet }
  240.     function    sendBackground( imageFilename : string ) : boolean;
  241.  
  242.     { send a string to stdout, as a comment.  This is used internally to
  243.       alert you to warnings/errors. }
  244.     procedure   sendComment( s : string );
  245.  
  246.     { send header, e.g. H1, H2, etc. }
  247.     function    sendHdr( hdrLevel : char; hdrText : string ) : boolean;
  248.  
  249.     { send horizonal ruler line command }
  250.     function    sendHR : boolean;
  251.  
  252.     { send A HREF command including optional image and optional (netscape) attributes
  253.       such as align=left or hspace=5 }
  254.     function    sendHREF( imageFilename : string; imageAttrib : string;
  255.                           visiblePhrase : string; linkedURL : string ) : boolean;
  256.  
  257.     { send a simple IMG SRC phase.  attrib can be hspace=5 or align=left }
  258.     function    sendIMG( imageFilename : string; imageAttrib : string ) : boolean;
  259.  
  260.     procedure   sendMailto( emailAddress : string );
  261.  
  262.     { do nothing; copied from Bob Denny's cgi.bas.  Bob Denny is the author of
  263.       Win-Httpd and WebSite server.  He has my endless gratitude for answering
  264.       my endless questions in May '95. }
  265.     procedure   sendNoOp;
  266.  
  267.     { send HTTP/1.0 200 OK etc. }
  268.     function    sendPrologue : boolean;
  269.  
  270.     { send TITLE phrase }
  271.     function    sendTitle( title : string ) : boolean;
  272.  
  273.     { convert Delphi date/time to GMT for use in HTML header }
  274.     function    webDate (dt : TDateTime ) : string ;
  275.  
  276.   end;
  277.  
  278. {***************************************************************}
  279. {***************************************************************}
  280.  
  281. type
  282.   TWebsite = class(TWebServer)
  283.   private
  284.     fServerType : NTWebServerType;
  285.     fCGI : TCGIEnvData;
  286.     fIniFile : TIniFile;
  287.     { custom }
  288.     procedure CGIData( p : pString; key : string; okEmpty : boolean );
  289.     procedure initData;
  290.     function  readWebSiteCGIString( key : string; okEmpty : boolean ) : string;
  291.     function  getExternalField( key : string; var externFilename : string; dest : PChar ) : boolean;
  292.     function  getTextArea( key : string; dest : TStringList ) : boolean;
  293.   public
  294.     { Public declarations }
  295.     property    INIFile: TIniFile read fIniFile stored false;
  296.     constructor Create(AOwner: TComponent); override;
  297.     destructor  Destroy; override;
  298.   published
  299.     function    getSmallField( key : string ) : string;
  300.     function    getSmallMultiField( key: string) :Tstringlist;     {*FWT*}
  301. end;
  302.  
  303. const
  304.      MAXTABLEFIELDS = 255;  { no more than 255 fields displayed in HTML Table }
  305.  
  306.      CGINOTFOUND = 'AAAKEY NOT FOUND';
  307.  
  308.      MAX_CMDARGS = 8;       { Max # of command line args }
  309.      ENUM_BUF_SIZE = 4096;  { Key enumeration buffer, see GetProfile() }
  310.      { These are the limits in the server }
  311.      MAX_XHDR = 100;        { Max # of "extra" request headers }
  312.      MAX_ACCTYPE = 100;     { Max # of Accept: types in request }
  313.      MAX_FORM_TUPLES = 100; { Max # form key=value pairs }
  314.      MAX_HUGE_TUPLES = 16;  { Max # "huge" form fields }
  315.  
  316. procedure closeApp( app : TApplication );
  317. procedure Register;
  318.  
  319. implementation
  320.  
  321. constructor TCGIEnvData.Create(AOwner: TComponent);
  322. begin
  323.  
  324.   inherited Create(AOwner);
  325.  
  326.     fStdOut := -99;
  327.     fAddress := '';
  328.  
  329.   { CGI section }
  330.     fCGICGIVersion := '';
  331.     fCGIRequestProtocol := '';
  332.     fCGIRequestMethod := '';
  333.     fCGIExecutablePath := '';
  334.     fCGILogicalPath := '';
  335.     fCGIPhysicalPath := '';
  336.     fCGIQueryString := '';
  337.     fCGIContentType := '';
  338.     fCGIContentLength := -1;   { init to -1 }
  339.     fCGIServerSoftware := '';
  340.     fCGIServerName := '';
  341.     fCGIServerPort := '';
  342.     fCGIServerAdmin := '';
  343.     fCGIReferer := '';
  344.     fCGIFrom := '';
  345.     fCGIRemoteHost := '';
  346.     fCGIRemoteAddress := '';
  347.     fCGIAuthenticatedUsername := '';
  348.     fCGIAuthenticatedPassword := '';
  349.     fCGIAuthenticationMethod  := '';
  350.     fCGIAuthenticationRealm   := '';
  351.  
  352.   { System section }
  353.     fSystemGMTOffset := 0;
  354.     fSystemOutputFile := '';
  355.     fSystemContentFile := '';
  356.     fSystemDebugMode := '';
  357.  
  358.     { Please realize that I thought there might be different
  359.       components for different web servers, and the correct one
  360.       would be linked in.  That whole strategy was abandoned. }
  361.     fServerComponent := nil;
  362.     if NOT (csDesigning in ComponentState) then
  363.       fServerComponent := TWebsite.create( self );
  364. end;
  365.  
  366. destructor TCGIEnvData.Destroy;
  367. begin
  368.      if fStdOut > 0 then
  369.         closeStdOut;
  370.      if NOT (csDesigning in ComponentState) then
  371.        fServerComponent.free;
  372.      inherited Destroy;
  373. end;
  374.  
  375. { **************************************************************
  376.           get CGI information from variables from INI file
  377.   ************************************************************** }
  378.  
  379. function  TCGIEnvData.getCGICGIVersion : pString;
  380. begin
  381.   result := addr( fCGICGIVersion );
  382. end;
  383.  
  384. procedure TCGIEnvData.getCGIItem( p : pString; key : string; okEmpty : boolean );
  385. var
  386.   x : TWebsite;
  387. begin
  388.   x := TWebsite( fServerComponent );
  389.   x.CGIData( p, key, okEmpty );
  390. end;
  391.  
  392. function  TCGIEnvData.getCGIRequestProtocol : pstring ;
  393. begin
  394.   getCGIitem( addr( fCGIRequestProtocol ), 'Request Protocol', TRUE );
  395.   result := addr( fCGIRequestProtocol );
  396. end;
  397.  
  398. function  TCGIEnvData.getCGIRequestMethod : pString;
  399. begin
  400.   result := addr( fCGIRequestMethod );
  401. end;
  402.  
  403. function  TCGIEnvData.getCGIExecutablePath : pString;
  404. begin
  405.   result := addr( fCGIExecutablePath );
  406. end;
  407.  
  408. function  TCGIEnvData.getCGILogicalPath : pstring ;
  409. begin
  410.   getCGIItem( addr( fCGILogicalPath ), 'Logical Path', FALSE );
  411.   result := addr( fCGILogicalPath );
  412. end;
  413.  
  414. function  TCGIEnvData.getCGIPhysicalPath : pString ;
  415. begin
  416.   getCGIItem( addr( fCGIPhysicalPath ), 'Physical Path', FALSE );
  417.   result := addr( fCGIPhysicalPath );
  418. end;
  419.  
  420. function  TCGIEnvData.getCGIQueryString : pString;
  421. begin
  422.   { it's because of QueryString being blank sometimes that the
  423.     okEmpty parameter was added throughout. }
  424.   getCGIItem( addr( fCGIQueryString ), 'Query String', TRUE );
  425.   result := addr( fCGIQueryString );
  426. end;
  427.  
  428. function  TCGIEnvData.getCGIContentType : pString;
  429. begin
  430.   getCGIItem( addr( fCGIContentType ), 'Content Type', FALSE );
  431.   result := addr( fCGIContentType );
  432. end;
  433.  
  434. function  TCGIEnvData.getCGIContentLength : longInt;
  435. var
  436.   x : TWebSite;
  437. begin
  438.   if fCGIContentLength <> -1 then begin
  439.     { we've already loaded the information }
  440.     result := fCGIContentLength;
  441.     exit;
  442.     end;
  443.   x := TWebsite( fServerComponent );
  444.   fCGIContentLength := x.fIniFile.readInteger( 'CGI', 'Content Length', 0 );
  445.   result := fCGIContentLength;
  446. end;
  447.  
  448. function  TCGIEnvData.getCGIServerSoftware : pString;
  449. begin
  450.   result := addr( fCGIServerSoftware );
  451. end;
  452.  
  453. function  TCGIEnvData.getCGIServerName : pstring ;
  454. begin
  455.   getCGIItem( addr( fCGIServerName ), 'Server Name', FALSE );
  456.   result := addr( fCGIServerName );
  457. end;
  458.  
  459. function  TCGIEnvData.getCGIServerPort : pstring ;
  460. begin
  461.   getCGIItem( addr( fCGIServerPort ), 'Server Name', FALSE );
  462.   result := addr( fCGIServerPort );
  463. end;
  464.  
  465. function  TCGIEnvData.getCGIServerAdmin : pString;
  466. begin
  467.   result := addr( fCGIServerAdmin );
  468. end;
  469.  
  470. function  TCGIEnvData.getCGIReferer : pstring ;
  471. var
  472.   x : TWebSite;
  473.  
  474. begin
  475.   getCGIItem( addr( fCGIReferer ), 'Referer', FALSE );
  476.   if fCGIReferer = cginotfound then begin
  477.     x := TWebsite( fServerComponent );
  478.     fCGIReferer := x.fIniFile.readString( 'Extra Headers', 'Referer', cginotfound );
  479.     end;
  480.   result := addr( fCGIReferer );
  481. end;
  482.  
  483. function  TCGIEnvData.getCGIFrom : pstring ;
  484. begin
  485.   getCGIItem( addr( fCGIFrom ), 'From', FALSE );
  486.   result := addr( fCGIFrom );
  487. end;
  488.  
  489. function  TCGIEnvData.getCGIRemoteHost : pstring ;
  490. begin
  491.   getCGIItem( addr( fCGIRemoteHost ), 'Remote Host', FALSE );
  492.   result := addr( fCGIRemoteHost );
  493. end;
  494.  
  495. function  TCGIEnvData.getCGIRemoteAddress : pstring ;
  496. begin
  497.   getCGIItem( addr( fCGIRemoteAddress ), 'Remote Address', FALSE );
  498.   result := addr( fCGIRemoteAddress );
  499. end;
  500.  
  501. function  TCGIEnvData.getCGIAuthenticatedUsername : pstring ;
  502. begin
  503.   getCGIItem( addr( fCGIAuthenticatedUsername ), 'Authenticated Username', TRUE );
  504.   result := addr( fCGIAuthenticatedUsername );
  505. end;
  506.  
  507. function  TCGIEnvData.getCGIAuthenticatedPassword : pstring ;
  508. begin
  509.   getCGIItem( addr( fCGIAuthenticatedPassword ), 'Authenticated Password', TRUE );
  510.   result := addr( fCGIAuthenticatedPassword );
  511. end;
  512.  
  513. function  TCGIEnvData.getCGIAuthenticationMethod : pstring ;
  514. begin
  515.   getCGIItem( addr( fCGIAuthenticationMethod ), 'Authentication Method', TRUE );
  516.   result := addr( fCGIAuthenticationMethod );
  517. end;
  518.  
  519. function  TCGIEnvData.getCGIAuthenticationRealm : pstring ;
  520. begin
  521.   getCGIItem( addr( fCGIAuthenticationRealm ), 'Authentication Realm', TRUE );
  522.   result := addr( fCGIAuthenticationRealm );
  523. end;
  524.  
  525. function  TCGIEnvData.getSectionValues( sectionName : string; strings : TStringList ) : boolean;
  526. var
  527.   x : TWebsite;
  528. begin
  529.   strings.clear;
  530.   x := TWebsite( fServerComponent );
  531.   x.fIniFile.readSectionValues( sectionName, strings );
  532.   result := (strings.count > 0);
  533. end;
  534.  
  535.  
  536. { **************************************************************
  537.           get SYSTEM information from variables from INI file
  538.   ************************************************************** }
  539.  
  540. function  TCGIEnvData.getSystemOutputFile : pString;
  541. begin
  542.   result := addr( fSystemOutputFile );
  543. end;
  544.  
  545. function  TCGIEnvData.getSystemContentFile : pstring ;
  546. var
  547.    x : TWebSite;
  548. begin
  549.   if fSystemContentFile = '' then begin
  550.     x := TWebsite( fServerComponent );
  551.     fSystemContentFile := x.fIniFile.readString( 'System', 'Content File', cginotfound );
  552.     end;
  553.   result := addr( fSystemContentFile );
  554. end;
  555.  
  556. function  TCGIEnvData.getSystemDebugMode : pstring ;
  557. var
  558.    x : TWebSite;
  559. begin
  560.   if fSystemDebugMode = '' then
  561.   begin
  562.     case fServerType of
  563.     webSite :
  564.       begin
  565.         x := TWebsite( fServerComponent );
  566.         fSystemDebugMode := x.fIniFile.readString( 'System', 'Debug Mode', cginotfound );
  567.       end;
  568.     else
  569.       raise exception.create( 'Can not get Debug Mode; invalid web server type' );
  570.     end;
  571.   end;
  572.   result := addr( fSystemDebugMode );
  573. end;
  574.  
  575. { Get the value of a "small" form field given the key
  576.   Signals an error if field does not exist }
  577. function TCGIEnvData.getSmallField( key : string ) : string;
  578. var
  579.    x : TWebsite;
  580.    FileName: string;
  581.    i,FileHandle:  integer;
  582.    read:          byte;
  583.    buffer:array[0..255] of char;
  584.    r1 : string;
  585. begin
  586.   x := TWebsite( fServerComponent );
  587.   result := x.getSmallField( key );
  588.   {************* code added to handle long or control chars **********FWT*}
  589.   if result = cginotfound then  begin
  590.       result := x.fIniFile.readString( 'Form External', key, cginotfound );
  591.       if result = cginotfound then
  592.           exit;
  593.       i := pos( ' ', result );
  594.       FileName := copy( result, 0, i - 1 );
  595.       i := strToInt( copy( result, i, 10 ) ) ;
  596.       read := 255;
  597.       FileHandle := fileOpen( FileName, fmOpenRead );
  598.       if FileHandle > 0 then begin
  599.           fileRead( FileHandle, buffer[0], read );
  600.           fileClose( FileHandle );
  601.           buffer[read] := #0;                     {mark the ending}
  602.           if i > read then begin                  {indicate truncation}
  603.             buffer[254] := '*';
  604.             buffer[255] := '$';
  605.             end
  606.           else begin
  607.             result := copy(strpas(buffer), 1, i);  {...'i' contains the correct string length...}
  608.             end
  609.           end
  610.         else
  611.           result := cginotfound;
  612.       end;
  613.  
  614.   {******************** end of code added for long or control chars***FWT*}
  615. end;
  616.  
  617. {************************** routine added - start of change **************FWT*}
  618. { Get the values of a "small" multiple selection form field given the key
  619.   Signals an error if field does not exist }
  620. function TCGIEnvData.getSmallMultiField( key : string ) : Tstringlist;
  621. var
  622.    x : TWebsite;
  623. begin
  624.   x := TWebsite( fServerComponent );
  625.   result := x.getSmallMultiField( key );
  626. end;
  627. {************************** routine added - end of change ***************FWT*}
  628.  
  629. function TCGIEnvData.getExternalField( key : string; var externFilename : string; dest : PChar ) : boolean;
  630. var
  631.   x : TWebsite;
  632. begin
  633.   x := TWebsite( fServerComponent );
  634.   result := x.getExternalField( key, externFilename, dest );
  635. end;
  636.  
  637. function TCGIEnvData.getTextArea( key : string; dest : TStringList ) : boolean;
  638. var
  639.   x : TWebsite;
  640. begin
  641.   x := TWebsite( fServerComponent );
  642.   result := x.getTextArea( key, dest );
  643. end;
  644.  
  645. { ************************************************************}
  646.  
  647. function TCGIEnvData.createStdout : boolean ;
  648. begin
  649.   { create output file and save pointer to it }
  650.   fStdout := fileCreate( fSystemOutputFile );
  651.   if fStdOut < 0 then begin
  652.     raise exception.create( 'Error code [' + intToStr( fStdOut ) +
  653.       '] when creating file (' + fSystemOutputFile + ')' );
  654.   end;
  655.   result := TRUE;
  656. end;
  657.  
  658.  
  659.  
  660. function TCGIEnvData.send( s : string ) : boolean ;
  661. begin
  662.  
  663.   result := sendString( s, TRUE );
  664.  
  665. end;
  666.  
  667. function TCGIEnvData.sendAuthRequest : boolean;
  668. begin
  669.  
  670.     closeStdout;
  671.     createStdout;
  672.  
  673.     result := send( 'HTTP/1.0 401 Unauthorized' );
  674.  
  675.     closeStdout;
  676.  
  677. end;
  678.  
  679. function TCGIEnvData.sendString( s : string; appendNewline : boolean ) : boolean;
  680. const
  681.   newLine : string[4] = #13#10;   {what's the minimum size here? 2? 3? 4? }
  682. var
  683.   s2 : string;
  684.   count : longInt;
  685. begin
  686.  
  687.   if fStdOut < 0 then
  688.      if NOT createStdout then
  689.        raise exception.create( 'Can not create stdout' );
  690.  
  691.   if appendNewline then
  692.     s2 := s + newLine
  693.   else
  694.     s2 := s; { will performance suffer? should there be a separate routine here? }
  695.  
  696.   count := length( s2 );
  697.  
  698.   { since the first byte of s2 contains the length, we shouldn't write
  699.   that out. Start instead with the next byte, which is s2[1]. }
  700.   result := ( fileWrite( fStdout, s2[1], count ) = count );
  701.  
  702. end;
  703.  
  704. procedure TCGIEnvData.closeStdout;
  705. begin
  706.      fileClose( fStdout );
  707. end;
  708.  
  709. { SendNoOp() - Tell browser to do nothing.
  710.   Most browsers will do nothing. Netscape 1.0N leaves hourglass
  711.   cursor until the mouse is waved around. Enhanced Mosaic 2.0
  712.   oputs up an alert saying "URL leads nowhere". Your results may
  713.   vary...}
  714. procedure TCGIEnvData.sendNoOp;
  715. begin
  716.     Send ('HTTP/1.0 204 No Response');
  717.     Send ('Server: ' + fCGIServerSoftware );
  718.     Send ('');
  719. end;
  720.  
  721. { WebDate - Return an HTTP/1.0 compliant date/time string
  722.  
  723.   Inputs:   dt = Local time as TDateTime (e.g., returned by Now)
  724.   Returns:  Properly formatted HTTP/1.0 date/time in GMT }
  725.  
  726. function TCGIEnvData.webDate (dt : TDateTime ) : String ;
  727. begin
  728.     WebDate := FormatDateTime('ddd dd mmm yyyy hh:mm:ss "GMT"',
  729.                dt - fSystemGMTOffset );
  730. end;
  731.  
  732. procedure TCGIEnvData.bounceToLocation( goHere : string );
  733. begin
  734.     closeStdout;
  735.     createStdout;
  736.     Send ('LOCATION: ' + goHere );
  737.     Send ('');
  738.     closeStdout;
  739. end;
  740.  
  741. function  TCGIEnvData.sendAddress : boolean;
  742. begin
  743.   if fAddress = '' then
  744.      result := FALSE
  745.   else
  746.       result := send( '<ADDRESS>' + fAddress + '</ADDRESS>' );
  747. end;
  748.  
  749. function  TCGIEnvData.sendHR : boolean;
  750. begin
  751.   result := send( '<HR>' );
  752. end;
  753.  
  754. function  TCGIEnvData.sendHdr( hdrLevel : char; hdrText : string ) : boolean;
  755. begin
  756.   if ( hdrLevel < '1' ) OR ( hdrLevel > '6' ) then
  757.   begin
  758.     sendComment( 'hdrLevel should be between 1 and 6.  Ref: ' + hdrText );
  759.     result := FALSE;
  760.   end
  761.   else
  762.     result := send( '<H' + hdrLevel + '>' + hdrText + '</H' + hdrLevel + '>' );
  763. end;
  764.  
  765. function  TCGIEnvData.sendHREF( imageFilename : string;
  766.                                     imageAttrib : string;
  767.                                     visiblePhrase : string;
  768.                                     linkedURL : string ) : boolean;
  769. begin
  770.  
  771.   if linkedURL = '' then begin
  772.      result := FALSE;
  773.      exit;
  774.     end;
  775.  
  776. { Here is a sample of what this can result in:
  777. <A HREF="http://www.sonic.net/~ann/htmlsmnr.html">
  778. <IMG SRC="/html/ann/infobahn.gif"
  779. >InfoBahn Construction Workshop</A>!
  780. }
  781.   send( '<A HREF="' + linkedURL + '">' );
  782.   if imageFilename <> '' then
  783.     send( '<IMG ' + imageAttrib + ' SRC="' + imageFilename + '">' );
  784.  
  785.   result := send( visiblePhrase + '</A>' );
  786. end;
  787.  
  788. function  TCGIEnvData.sendIMG( imageFilename : string; imageAttrib : string ) : boolean;
  789. begin
  790.   result := send( '<IMG ' + imageAttrib + ' SRC="' + imageFilename + '">' );
  791. end;
  792.  
  793. function  TCGIEnvData.sendPrologue : boolean;
  794. begin
  795.   try
  796.     send( 'HTTP/1.0 200 OK' );
  797.     send( 'SERVER: ' + fCGIServerSoftware );
  798.     send( 'DATE: ' + webDate( now ) );
  799.     send( 'Content-type: text/html' );
  800.     send( '' );          { required blank line }
  801.     result := TRUE;
  802.   except
  803.     result := FALSE;
  804.   end;
  805. end;
  806.  
  807.  
  808. function  TCGIEnvData.sendTitle( title : string ) : boolean;
  809. begin
  810.   result := send( '<TITLE>' + title + '</TITLE>' );
  811. end;
  812.  
  813. function  TCGIEnvData.sendBackground( imageFilename : string ) : boolean;
  814. begin
  815.   {<body background="bkground.gif">}
  816.   result := send( '<BODY BACKGROUND="' + imageFilename + '"' );
  817. end;
  818.  
  819. procedure TCGIEnvData.sendComment( s : string );
  820. begin
  821.   send( '<!-- ' + s + ' -->' );
  822. end;
  823.  
  824. procedure TCGIEnvData.sendMailto( emailAddress : string );
  825. begin
  826.   send( '<A HREF="mailto:' + emailAddress + '">' + emailAddress + '</A>' );
  827. end;
  828.  
  829. procedure TCGIEnvData.cgiErrorHandler( sender: TObject; e : Exception );
  830. begin
  831.      if fStdout = -99 then
  832.         { haven't even gotten as far as opening stdout at all yet! }
  833.         { this would be a bad time to count on writing to that file !! }
  834.        closeApp( application );
  835. try
  836.     createStdout;
  837.     Send ('HTTP/1.0 500 Internal Error');
  838.     Send ('SERVER: ' + fCGIServerSoftware);
  839.     Send ('DATE: ' + WebDate(Now) );
  840.     Send ('Content-type: text/html' );
  841.     Send ('');
  842.     Send ('<HTML><HEAD>');
  843.     Send ('<TITLE>Error in ' + fCGIExecutablePath + '</TITLE>' );
  844.     Send ('</HEAD><BODY>');
  845.     SendHdr( '2', 'Error in ' + fCGIExecutablePath );
  846.     Send ('An internal error has occurred in this program: ' + fCGIExecutablePath + '.');
  847.     Send ('<PRE>' + e.message + '</PRE>');
  848.     Send ('<I>Please</I> note what you were doing when this problem occurred, ');
  849.     Send ('so we can identify and correct it. Write down the Web page you were using, ');
  850.     Send ('any data you may have entered into a form or search box, the' );
  851.     Send ('date and time listed below, and ');
  852.     Send ('anything else that may help us duplicate the problem. Then contact the ');
  853.     Send ('administrator of this service: ');
  854.     Send ('<A HREF="mailto:' + fCGIServerAdmin + '">' + fCGIServerAdmin + '</A> ' );
  855.     SendHR;
  856.     send( 'Generated on: ' + webDate( now ) );
  857.     Send ('</BODY></HTML>');
  858.     fileClose( fStdOut );
  859.     fStdOut := -99;
  860. finally
  861.   { the bottom line! }
  862.     closeApp( application );
  863. end;
  864.  
  865. end;
  866.  
  867. procedure TCGIEnvData.setIniFilename( value : string );
  868. var
  869.    x : TWebSite;
  870. begin
  871.   fINIFilename := value;
  872.   if NOT ( csDesigning in componentState ) then begin
  873.     x := TWebSite( fServerComponent );
  874.     x.initData;
  875.     end;
  876. end;
  877.  
  878. function TCGIEnvData.swapChar( s : string; fromChar : char; toChar : char ) : string;
  879. var
  880.   i : shortint;
  881. begin
  882.   for i := 1 to length( s ) do
  883.     if s[i] = fromChar then
  884.       s[i] := toChar;
  885.   result := s;
  886. end;
  887.  
  888. {***************************************************************}
  889. {***************************************************************}
  890.  
  891. constructor TWebsite.create(AOwner: TComponent);
  892. begin
  893.   if AOwner = nil then
  894.     raise exception.create( 'Tried to create TWebsite object with nil owner.' );
  895.  
  896.   inherited Create(AOwner);
  897.  
  898.   fIniFile := nil;
  899.   fServerType := WebSite;
  900.  
  901.   { this works only if AOwner is a valid pointer, which it should be
  902.   since we're only created from within a CGIEnvData component }
  903.   fCGI := TCGIEnvData(AOwner);  { connect back to CGIEnvData }
  904. end;
  905.  
  906. procedure TWebSite.initData;
  907. begin
  908.   if fCGI.WebSiteINIFilename = '' then
  909.     raise exception.create( 'WebSiteINIFilename is blank' );
  910.  
  911.   try
  912.      { create pointer to INI file }
  913.      fIniFile := tInifile.create( fCGI.WebSiteIniFilename );
  914.   except
  915.      raise exception.create( 'Can not create tIniFile object' );
  916.   end;
  917.  
  918.   with fCGI do begin
  919.     { [CGI]                <== The standard CGI variables }
  920.     fCGICGIVersion     := readWebSiteCGIString( 'CGI Version', FALSE );
  921.     fCGIRequestMethod  := readWebSiteCGIString( 'Request Method', FALSE );
  922.     { Request Protocol handled elsewhere }
  923.     fCGIExecutablePath := readWebSiteCGIString( 'Executable Path', FALSE );
  924.  
  925.     fCGIServerSoftware := readWebSiteCGIString( 'Server Software', FALSE );
  926.     fCGIServerAdmin    := readWebSiteCGIString( 'Server Admin', TRUE );
  927.     end;
  928.  
  929.   with fIniFile do begin
  930.     { [System]             <== Windows interface specifics }
  931.     { in visual basic: CGI_GMTOffset = CVDate(CDbl(buf) / 86400#)' Timeserial offset }
  932.     fCGI.fSystemGMToffset := ( readInteger( 'System', 'GMT Offset', 0 ) / 86400 );  { fixed 6/12/95 aml }
  933.     fCGI.fSystemOutputFile  := readString( 'System', 'Output File', 'ann_x.out' );
  934.     fCGI.fSystemContentFile := readString( 'System', 'Content File', '' );
  935.   end;
  936. end;
  937.  
  938. destructor TWebsite.Destroy;
  939. begin
  940.      fIniFile.free;
  941.      inherited Destroy;
  942. end;
  943.  
  944. function TWebsite.readWebSiteCGIString( key : string; okEmpty : boolean ) : string;
  945. begin
  946.   result := fINIfile.readString( 'CGI', key, cginotfound );
  947. { notfound is not always bad, e.g. user might not be authenticated first time around }
  948.    if result = cginotfound then
  949.      if NOT okEmpty then
  950.        fCGI.sendComment( '[CGI] ' + key + ' key not found in WebSite INI file' );
  951. end;
  952.  
  953. procedure TWebsite.CGIData( p : pString; key : string; okEmpty : boolean );
  954. begin
  955.      if p^ = '' then
  956.         p^ := readWebSiteCGIString( key, okEmpty );
  957. end;
  958.  
  959. { returns KEY NOT FOUND and logs sendComment if that happens; otherwise full text }
  960. function TWebsite.getSmallField( key : string ) : string;
  961. begin
  962.   with fIniFile do
  963.     result := readString( 'Form Literal', key, cginotfound );
  964.  
  965.   if result = cginotfound then
  966.     fCGI.sendComment( 'Field ' + key + ' is not in [Form Literal] section of WebSite .ini file.' );
  967. end;
  968.  
  969. { returns KEY NOT FOUND and logs sendComment if that happens; otherwise full text }
  970. function TWebsite.getSmallMultiField( key : string ) : Tstringlist;
  971. var
  972.   varval, varname: string;
  973. begin
  974.   result := TStringList.create;
  975.   varname := key;
  976.   varval  := 'start';
  977.   while varval <> cginotfound do begin
  978.     with fIniFile do
  979.       varval := readString( 'Form Literal', varname, cginotfound );
  980.       if varval <> cginotfound then begin
  981.           result.add( varval );
  982.           varname := key+'_'+IntToStr(result.count);
  983.           end;
  984.     end;
  985. end;
  986.  
  987. { if key not found, then 3 things happen.  1. returns false
  988.   2. externFilename set to ''   3. error comment sent out }
  989. function TWebsite.getExternalField( key : string;
  990.                                     var externFilename : string;
  991.                                     dest : PChar ) : boolean;
  992. var
  993.   info : string;
  994.   buffer : string;
  995.   x : byte;
  996.   dataSize : integer;
  997.   fileHandle : integer;
  998.  
  999. begin
  1000.  
  1001. { [Form External]  notes written by Bob Denny and included in cgi.bas
  1002.   If the decoded value string is more than 254 characters long,
  1003.   or if the decoded value string contains any control characters,
  1004.   the server puts the decoded value into an external tempfile and
  1005.   lists the field in this section as:
  1006.      key=<pathname> <length>
  1007.   where <pathname> is the path and name of the tempfile containing
  1008.   the decoded value string, and <length> is the length in bytes
  1009.   of the decoded value string.
  1010.  
  1011.   Data larger than 65,536 bytes goes to [Form Huge] section. }
  1012.  
  1013.      with fIniFile do
  1014.        info := readString( 'Form External', key, cginotfound );
  1015.  
  1016.      if info = cginotfound then
  1017.      begin
  1018.        result := FALSE;
  1019.        externFilename := '';
  1020.        fCGI.sendComment( 'Field ' + key + ' is not in [Form External] section of WebSite .ini file.' );
  1021.        exit;
  1022.      end;
  1023.  
  1024.      x := pos( ' ', info );
  1025.      externFilename := copy( info, 0, x - 1 );
  1026.  
  1027.      dataSize := strToInt( copy( info, x, 10 ) ) ;
  1028.      dest := strAlloc( dataSize + 1 );
  1029.  
  1030.      { !!! need more error checking in this routine }
  1031.      fileHandle := fileOpen( externFilename, fmOpenRead );
  1032.      fileRead( fileHandle, dest, dataSize );
  1033.      fileClose( fileHandle );
  1034.      result := TRUE;
  1035. end;
  1036.  
  1037. function TWebsite.getTextArea( key : string; dest : TStringList ) : boolean;
  1038. var
  1039.   info : string;
  1040.   buffer : string;
  1041.   x : byte;
  1042.   dataSize : integer;
  1043.   f : textFile;
  1044.   externfilename : string;
  1045.  
  1046. begin
  1047.  
  1048.      result := TRUE;
  1049.  
  1050.      if dest = nil then
  1051.      begin
  1052.        dest := TStringList.create;
  1053.        fCGI.sendComment( 'TstringList was nil in call to getExternalStrList.  ' +
  1054.                             'You should be using TStringList.create and .free yourself.' );
  1055.      end;
  1056.  
  1057.      dest.clear;
  1058.  
  1059.      { first see whether it's there as a one-liner }
  1060.      buffer := getSmallField( key );
  1061.      if buffer <> cginotfound then
  1062.      begin
  1063.        dest.add( buffer );    { all done }
  1064.        exit;
  1065.      end;
  1066.  
  1067.  
  1068.      with fIniFile do
  1069.        info := readString( 'Form External', key, cginotfound );
  1070.  
  1071.      if info = cginotfound then
  1072.      begin
  1073.        result := FALSE;
  1074.        fCGI.sendComment( 'Field ' + key + ' is not in [Form External] section of WebSite .ini file.' );
  1075.        exit;
  1076.      end;
  1077.  
  1078.      x := pos( ' ', info );
  1079.      externFilename := copy( info, 0, x - 1 );
  1080.  
  1081.      dataSize := strToInt( copy( info, x, 10 ) ) ;
  1082.  
  1083.      { !!! need more error checking in this routine }
  1084.      assignFile( f, externFilename );
  1085.      reset(f);
  1086.      while NOT eof(f) do
  1087.      begin
  1088.        readLn( f, buffer );
  1089.        dest.add( buffer );
  1090.      end;
  1091.      closeFile( f );
  1092.      result := TRUE;
  1093.  
  1094. end;
  1095.  
  1096. procedure closeApp( app : TApplication );
  1097. begin
  1098.   {Thanks to Charlie Calvert for the postMessage syntax. }
  1099.   { FYI: app.close; doesn't work and halt(1) is bad because resources aren't freed. }
  1100.   postMessage( app.Handle, wm_Close, 0, 0);
  1101. end;
  1102.  
  1103. {***************************************************************}
  1104. {***************************************************************}
  1105.  
  1106. procedure Register;
  1107. begin
  1108.   RegisterComponents('CGI', [TCGIEnvData]);
  1109. end;
  1110.  
  1111. end.
  1112.  
  1113.